//================================
//================================
//   
//   -   jp_admin.c
//      In-game Administration system
//   
//   Author: Raz0r
//   
//   Desc:   This administration system is developed to be flexible,
//         easy to maintain, secure and simple.
//         
//         There is a list of user/pass combinations to be defined by
//         the server owner, with each user having their own permission mask.
//         
//         All checking of permissions is done before the associated function
//         is called, so you don't have to worry about complex control paths
//         
//================================
//================================

#include "g_local.h"
#include "jp_admin.h"
#include "jp_tokenParser.h"

//================================
//   Begin parsing
//================================

#define MAX_ADMINS 64
static adminUser_t   adminUsers[MAX_ADMINS];
static int         adminUsersSize;

static qboolean AM_ProcessUser( const char *token )
{
   const char   *tmp;
   adminUser_t   *current = &adminUsers[adminUsersSize];

   //If we're still at the end of the previous telemark, there's no more :o
   if ( !Q_stricmp( token, "}" ) )
      return qfalse;

   //Name
   if ( TP_ParseString( &tmp ) )
      Com_Printf( "Unexpected EOL line %i (Expected 'user')\n", TP_CurrentLine() );
   else
      Q_strncpyz( current->user, tmp, sizeof( current->user ) );

   //IP
   if ( TP_ParseString( &tmp ) )
      Com_Printf( "Unexpected EOL line %i (Expected 'password')\n", TP_CurrentLine() );
   else
      Q_strncpyz( current->password, tmp, sizeof( current->password ) );

   //Admin password
   if ( TP_ParseUInt( &current->privs ) )
      Com_Printf( "Unexpected EOL line %i (Expected 'privs')\n", TP_CurrentLine() );

   //Successful write, fix index
   adminUsersSize++;

   token = TP_ParseToken();
   return qtrue;
}

void AM_ParseAdmins( void )
{
   char         *buf;
   char         loadPath[MAX_QPATH];
   unsigned int   len;
   const char      *token;
   fileHandle_t   f;

   //Clear the admins table
   memset( &adminUsers, 0, sizeof( adminUser_t ) * 64 );
   adminUsersSize = 0;

   Com_Printf( "^5JA++: Loading Admin Data...\n" );
   Com_sprintf( loadPath, sizeof(loadPath), "admins.dat" );
   len = trap_FS_FOpenFile( loadPath, &f, FS_READ );

   if ( !f )
   {//no file
      Com_Printf( "^1Admin loading failed! (Can't find %s)\n", loadPath );
      return;
   }

   if ( !len || len == -1 )
   {//empty file
      Com_Printf( "^1Admin loading failed! (%s is empty)\n", loadPath );
      trap_FS_FCloseFile( f );
      return;
   }

   if ( (buf = (char*)malloc(len+1)) == 0 )
   {//alloc memory for buffer
      Com_Printf( "^1Admin loading failed! (Failed to allocate buffer)\n" );
      return;
   }

   trap_FS_Read( buf, len, f );
   trap_FS_FCloseFile( f );
   buf[len] = 0;//Add null terminator o_o'
   TP_NewParseSession( buf );

   while ( 1 )
   {//the file is there, and it's not empty - don't give up until we find what we want!
      token = TP_ParseToken();
      if ( !token[0] )
         break;
      
      if ( adminUsersSize < MAX_ADMINS )
      {
         if ( !AM_ProcessUser( token ) )
            break;
         continue;
      }

      Com_Printf( "JA++: AM_ParseAdmins() MAX_ADMINS reached!" );
   }

   // If we get here, it all went just fine
   free( buf );//free our memory! (it has rights ^_^)

   return;
}

static const adminCommand_t adminCommands[] =
{
   //House-keeping
//   {   "amlogin",      -1,            AM_Login         },   //   Log in using user + pass (Handled explicitly!!!)
   {   "amlogout",      -1,            AM_Logout         },   //   Logout

   {   "guntele",      PRIV_TELEPORT,   AM_GunTeleport      },   //   Teleport self to targeted position
};
static const int adminCommandsSize = sizeof( adminCommands ) / sizeof( adminCommands[0] );

//   Handle admin related commands.
//   Return qtrue if the command exists and/or everything was handled fine.
//   Return qfalse if command didn't exist, so we can tell them.
qboolean AM_HandleCommands( gentity_t *ent, const char *cmd )
{
   int i;

   if ( !Q_stricmp( cmd, "amlogin" ) )
   {
      AM_Login( ent );
      return qtrue;
   }

   //RAZTODO: hashmap please
   for ( i=0; i<adminCommandsSize; i++ )
      if ( !Q_stricmp( cmd, adminCommands[i].cmd ) )
         AM_EXECUTE( ent, adminCommands[i].privs, adminCommands[i].func )

   return qfalse;

NotAllowed:
   trap_SendServerCommand( ent-g_entities, "print \"You're not allowed to execute this command!\n\"" );
   return qtrue;
}

#ifdef _DEBUG
   extern void G_TestLine(vec3_t start, vec3_t end, int color, int time);
#endif
trace_t *RealTrace( gentity_t *ent, float dist )
{
static   trace_t tr;
      vec3_t   start, end;

   //Get start
   VectorCopy( ent->client->ps.origin, start );
   start[2] += ent->client->ps.viewheight; //36.0f;

   //Get end
   AngleVectors( ent->client->ps.viewangles, end, NULL, NULL );
   VectorMA( start, dist ? dist : 16384.0f, end, end );

   //Trace
   trap_Trace( &tr, start, NULL, NULL, end, ent->s.number, 1184515/*MASK_OPAQUE|CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE*/ );

   #ifdef _DEBUG
      G_TestLine( start, tr.endpos, 0xFF, 7500 );
   #endif

   return &tr;
}

//================================
//   House-keeping
//================================

void AM_Login( gentity_t *ent )
{//Log in using user + pass (Handled explicitly!!!)
   char   user[64];
   char   pass[64];
   int      i;
   qboolean   loginSuccess = qfalse;

   if ( trap_Argc() < 3 )
   {
      trap_SendServerCommand( ent-g_entities, "print \"Please enter a name and password to login\n\"" );
      return;
   }

   //Grab user + pass
   trap_Argv( 1, user, 64 );
   trap_Argv( 2, pass, 64 );

   //Log in!
   for (i=0; i<adminUsersSize; i++)
   {
      if ( !strcmp( adminUsers[i].user, user ) )
      {
         if ( !strcmp( adminUsers[i].password, pass ) )
         {
            ent->client->pers.adminUser = &adminUsers[i];
            loginSuccess = qtrue;
            break;
         }
      }
   }

   //If it failed, let them know
   if ( loginSuccess )
      trap_SendServerCommand( -1, va( "print \"%s ^7has logged in\n\"", ent->client->pers.netname ) );
   else
      trap_SendServerCommand( ent-g_entities, "print \"Invalid login\n\"" );
}

void AM_Logout( gentity_t *ent )
{//Logout
   ent->client->pers.adminUser = NULL;
   trap_SendServerCommand( ent-g_entities, "print \"You have logged out\n\"" );
}

void AM_GunTeleport( gentity_t *ent )
{//Teleport self to targeted position
   trace_t   *tr = RealTrace( ent, 0.0f );
   vec3_t   telepos;

   VectorMA( tr->endpos, 48.0f, tr->plane.normal, telepos );
   TeleportPlayer( ent, telepos, ent->client->ps.viewangles );
}